package com.dynamic.data.permission.interceptor;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.core.enums.SqlKeyword;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.dynamic.data.permission.annotation.ResourceFieldCheck;
import com.dynamic.data.permission.annotation.ResourceTableCheck;
import com.dynamic.data.permission.common.enums.ResoureFieldType;
import com.dynamic.data.permission.model.ResourceCheckHolder;
import com.dynamic.data.permission.model.UserContextHolder;
import com.dynamic.data.permission.model.UserInfoContext;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.ExistsExpression;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.ItemsList;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.*;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.*;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.text.SimpleDateFormat;
import java.util.*;

@Slf4j
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
@Component
public class ResourceCodeInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) {
        try {
            // 检查当前请求的方法是否带有 @ResourceCheck 注解
            if (!Boolean.TRUE.equals(ResourceCheckHolder.get())) {
                return invocation.proceed();
            }
            // 获取 StatementHandler 对象
            StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
            BoundSql boundSql = statementHandler.getBoundSql();
            String originalSql = boundSql.getSql();

            // 动态添加 resourceCode 条件
            String modifiedSql = addResourceCodeCondition(originalSql);

            // 替换原始 SQL
            PluginUtils.mpBoundSql(boundSql).sql(modifiedSql);

            // 继续执行后续逻辑
            return invocation.proceed();
        } catch (Throwable tb) {
            log.error("ResourceCodeInterceptor is fatal error", tb);
            try {
                return invocation.proceed();
            } catch (InvocationTargetException | IllegalAccessException e) {
                log.error("invocation.proceed() is fatal error", e);
                throw new RuntimeException(e);
            }
        }

    }

    /**
     * 动态添加 resourceCode 条件
     */
    private String addResourceCodeCondition(String originalSql) throws JSQLParserException {
        log.info("before modify sql：{}", originalSql);
        // 使用 JSqlParser 解析 SQL
        Statement statement = CCJSqlParserUtil.parse(originalSql);
        // 处理 SELECT 查询
        if (statement instanceof Select) {
            processSelect((Select) statement);
        }
        // 返回修改后的 SQL
        log.info("after modify sql：{}", statement.toString());
        return statement.toString();

    }

    /**
     * 处理 SELECT 查询
     */
    private void processSelect(Select select) throws JSQLParserException {
        SelectBody selectBody = select.getSelectBody();
        if (selectBody instanceof PlainSelect) {
            processPlainSelect((PlainSelect) selectBody);
        } else if (selectBody instanceof SetOperationList) {
            processSetOperationList((SetOperationList) selectBody);
        }
    }

    /**
     * 处理普通查询 (PlainSelect)
     */
    private void processPlainSelect(PlainSelect plainSelect) throws JSQLParserException {
        // 构建表名与别名的映射
        Map<String, String> tableAliasMap = buildTableAliasMap(plainSelect);

        // 检查主查询的表是否需要权限校验
        FromItem fromItem = plainSelect.getFromItem();
        processFromItem(fromItem, tableAliasMap, plainSelect);

        // 处理 JOIN 子句中的表
        if (plainSelect.getJoins() != null) {
            for (Join join : plainSelect.getJoins()) {
                processFromItem(join.getRightItem(), tableAliasMap, plainSelect);
            }
        }
        // 处理 where 条件中的子查询
        Expression where = plainSelect.getWhere();
        if (where != null) {
            processExpression(where, tableAliasMap, plainSelect);
        }
    }

    /**
     * 递归处理表达式中的子查询
     */
    private void processExpression(Expression expression, Map<String, String> tableAliasMap, PlainSelect plainSelect) throws JSQLParserException {
        if (expression instanceof SubSelect) {
            processSubSelect((SubSelect) expression);
        } else if (expression instanceof AndExpression) {
            AndExpression andExpression = (AndExpression) expression;
            processExpression(andExpression.getLeftExpression(), tableAliasMap, plainSelect);
            processExpression(andExpression.getRightExpression(), tableAliasMap, plainSelect);
        } else if (expression instanceof InExpression) {
            InExpression inExpression = (InExpression) expression;
            ItemsList itemsList = inExpression.getRightItemsList();
            if (itemsList instanceof SubSelect) {
                processSubSelect((SubSelect) itemsList);
            }
        } else if (expression instanceof ExistsExpression) {
            ExistsExpression existsExpression = (ExistsExpression) expression;
            processExpression(existsExpression.getRightExpression(), tableAliasMap, plainSelect);
        } else if (expression instanceof OrExpression) {
            OrExpression orExpression = (OrExpression) expression;
            processExpression(orExpression.getLeftExpression(), tableAliasMap, plainSelect);
            processExpression(orExpression.getRightExpression(), tableAliasMap, plainSelect);
        }
    }

    /**
     * 处理 FROM 子句中的表或子查询
     */
    private void processFromItem(FromItem fromItem, Map<String, String> tableAliasMap, PlainSelect plainSelect) throws JSQLParserException {
        if (fromItem instanceof Table) {
            Table table = (Table) fromItem;
            String tableName = table.getName();
            String alias = table.getAlias() != null ? table.getAlias().getName() : tableName;

            if (isTableNeedResourceCheck(tableName)) {
                addWhereCondition(plainSelect, tableName, alias);
            }
        } else if (fromItem instanceof SubSelect) {
            processSubSelect((SubSelect) fromItem);
        }
    }

    /**
     * 处理集合操作 (UNION, INTERSECT, EXCEPT)
     */
    private void processSetOperationList(SetOperationList setOperationList) {
        setOperationList.getSelects().forEach(selectBody -> {
            if (selectBody instanceof PlainSelect) {
                try {
                    processPlainSelect((PlainSelect) selectBody);
                } catch (JSQLParserException e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }

    /**
     * 处理子查询
     */
    private void processSubSelect(SubSelect subSelect) throws JSQLParserException {
        SelectBody subSelectBody = subSelect.getSelectBody();
        if (subSelectBody instanceof PlainSelect) {
            processPlainSelect((PlainSelect) subSelectBody);
        }
    }

    /**
     * 添加 WHERE 条件
     */
    private void addWhereCondition(PlainSelect plainSelect, String tableName, String alias) throws JSQLParserException {
        UserInfoContext userInfoContext = UserContextHolder.get();
        if (userInfoContext == null || userInfoContext.getUserId() == null || userInfoContext.getRegisterTime() == null) {
            return;
        }
        //获取字段名
        List<Condition> conditions = getResourceCheckFields(tableName);
        if (conditions.isEmpty()) {
            return;
        }
        for (Condition condition : conditions) {
            ResoureFieldType resoureFieldType = condition.getFieldValueType();
            switch (resoureFieldType) {
                case USER_ID:
                    Long userId = userInfoContext.getUserId();
                    if (userId != null) {
                        addCondition(plainSelect, alias, condition.fieldName, userId, condition.getCompare());
                    }
                    break;
                case START_TIME:
                    String startTime = dateToString(userInfoContext.getRegisterTime());
                    if (StringUtils.hasLength(startTime)) {
                        addCondition(plainSelect, alias, condition.fieldName, startTime, condition.getCompare());
                    }
                    break;
            }
        }

    }

    /**
     * 构造 查询 条件
     */
    private void addCondition(PlainSelect plainSelect, String tableName, String fieldName, Object fieldVale, SqlKeyword compare) throws JSQLParserException {
        // 构造 resourceCode 条件
        Expression condition = CCJSqlParserUtil.parseCondExpression(buildCondition(tableName, fieldName, fieldVale, compare));
        // 如果已经有 WHERE 条件，则使用 AND 连接
        if (plainSelect.getWhere() != null) {
            plainSelect.setWhere(new AndExpression(plainSelect.getWhere(), condition));
        } else {
            plainSelect.setWhere(condition);
        }
    }

    /**
     * 判断表是否需要权限校验
     */
    private boolean isTableNeedResourceCheck(String tableName) {
        // 获取所有实体类的 TableInfo
        TableInfo tableInfo = TableInfoHelper.getTableInfo(tableName);
        if (tableInfo == null) {
            return false;
        }
        // 获取实体类的 Class 对象
        Class<?> entityClass = tableInfo.getEntityType();
        // 检查实体类是否有 @ResourceCheck 注解
        return entityClass.isAnnotationPresent(ResourceTableCheck.class);
    }

    /**
     * 日期转字符串类型
     */
    private String dateToString(Date date) {
        if (date == null) {
            return null;
        }
        // 定义日期格式
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        // 将 Date 转换为字符串
        return sdf.format(date);
    }

    /**
     * 模拟获取当前用户的 resourceCode
     */
    private String getCurrentUserResourceCode() {
        // 实际项目中可以从 SecurityContext 或其他上下文中获取
        return null; // 示例值
    }

    /**
     * 构建表名与别名的映射
     */
    private Map<String, String> buildTableAliasMap(PlainSelect plainSelect) {
        Map<String, String> tableAliasMap = new HashMap<>();

        // 主查询的 FROM 子句
        FromItem fromItem = plainSelect.getFromItem();
        if (fromItem instanceof Table) {
            Table table = (Table) fromItem;
            String tableName = table.getName();
            String alias = table.getAlias() != null ? table.getAlias().getName() : tableName;
            tableAliasMap.put(tableName, alias);
        }

        // JOIN 子句
        if (plainSelect.getJoins() != null) {
            plainSelect.getJoins().forEach(join -> {
                if (join.getRightItem() instanceof Table) {
                    Table table = (Table) join.getRightItem();
                    String tableName = table.getName();
                    String alias = table.getAlias() != null ? table.getAlias().getName() : tableName;
                    tableAliasMap.put(tableName, alias);
                }
            });
        }

        return tableAliasMap;
    }

    /**
     * 获取实体类上标记 @ResourceCheckField 的字段名
     */
    private List<Condition> getResourceCheckFields(String tableName) {
        TableInfo tableInfo = TableInfoHelper.getTableInfo(tableName);
        if (tableInfo == null) {
            return Collections.emptyList();
        }
        Class<?> entityClass = tableInfo.getEntityType();
        List<Condition> conditions = new ArrayList<>();
        for (Field field : entityClass.getDeclaredFields()) {
            ResourceFieldCheck fieldCheck = field.getAnnotation(ResourceFieldCheck.class);
            if (fieldCheck != null) {
                String fieldName = field.getName();
                if (field.isAnnotationPresent(TableField.class)) {
                    // 获取 @TableField 注解实例
                    TableField tableField = field.getAnnotation(TableField.class);
                    fieldName = tableField.value();
                } else {
                    fieldName = convertToSnakeCase(fieldName);
                }
                Condition condition = new Condition();
                condition.setFieldName(fieldName);
                condition.setFieldValueType(fieldCheck.code());
                condition.setCompare(fieldCheck.compare());
                conditions.add(condition);
            }
        }
        return conditions;
    }

    // 动态拼接 SQL 条件的方法
    public static String buildCondition(String tableName, String columnName, Object value, SqlKeyword operator) {
        StringBuilder condition = new StringBuilder();
        condition.append(tableName).append(".");
        condition.append(columnName).append(" ");
        // 根据操作符获取对应的 SQL 关键字
        String operatorStr = operator.getSqlSegment();
        condition.append(operatorStr).append(" ");

        // 处理不同类型的操作符和值
        if (operator == SqlKeyword.IN || operator == SqlKeyword.NOT_IN) {
            if (value instanceof List) {
                List<?> valueList = (List<?>) value;
                condition.append("(");
                for (int i = 0; i < valueList.size(); i++) {
                    if (i > 0) {
                        condition.append(",");
                    }
                    condition.append(formatValue(valueList.get(i)));
                }
                condition.append(")");
            } else {
                throw new IllegalArgumentException("For IN or NOT IN operator, the value should be a List.");
            }
        } else if (operator == SqlKeyword.BETWEEN || operator == SqlKeyword.NOT_BETWEEN) {
            if (value instanceof List && ((List<?>) value).size() == 2) {
                List<?> valueList = (List<?>) value;
                condition.append(formatValue(valueList.get(0))).append(" AND ").append(formatValue(valueList.get(1)));
            } else {
                throw new IllegalArgumentException("For BETWEEN or NOT BETWEEN operator, the value should be a List of size 2.");
            }
        } else {
            condition.append(formatValue(value));
        }
        return condition.toString();
    }

    // 格式化值的方法，处理字符串类型的值需要添加引号
    private static String formatValue(Object value) {
        if (value instanceof String) {
            return "'" + value + "'";
        }
        return String.valueOf(value);
    }

    /**
     * 将驼峰命名的字符串转换为下划线命名的字符串
     *
     * @param camelCase 驼峰命名的字符串
     * @return 下划线命名的字符串
     */
    public String convertToSnakeCase(String camelCase) {
        if (camelCase == null || camelCase.isEmpty()) {
            return camelCase;
        }
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < camelCase.length(); i++) {
            char c = camelCase.charAt(i);
            if (Character.isUpperCase(c)) {
                // 如果是大写字母，在前面插入下划线，并将其转换为小写
                result.append("_").append(Character.toLowerCase(c));
            } else {
                result.append(c);
            }
        }
        return result.toString();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        // 可以设置一些自定义属性
    }

    @Data
    class Condition {

        private String fieldName;

        private ResoureFieldType fieldValueType;

        private SqlKeyword compare;
    }
}